```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GeoHash技术解析：打车App如何秒级找到附近乘客</title>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', 'Noto Serif SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.8;
        }
        .hero-gradient {
            background: linear-gradient(135deg, #3a7bd5 0%, #00d2ff 100%);
        }
        .code-block {
            background-color: #2d2d2d;
            border-radius: 8px;
            position: relative;
        }
        .code-header {
            background-color: #1e1e1e;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
            padding: 8px 16px;
            color: #9cdcfe;
            font-size: 0.9rem;
            display: flex;
            justify-content: space-between;
        }
        .code-content {
            padding: 16px;
            overflow-x: auto;
        }
        .feature-card {
            transition: all 0.3s ease;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0,0,0,0.08);
        }
        .feature-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 30px rgba(0,0,0,0.12);
        }
        .section-divider {
            height: 1px;
            background: linear-gradient(90deg, transparent, rgba(0,0,0,0.1), transparent);
            margin: 60px 0;
        }
        .highlight-box {
            border-left: 4px solid #3a7bd5;
            background-color: rgba(58, 123, 213, 0.05);
        }
        .table-compare {
            border-collapse: separate;
            border-spacing: 0;
            width: 100%;
        }
        .table-compare th, .table-compare td {
            padding: 12px 16px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }
        .table-compare th {
            background-color: #f8f9fa;
            font-weight: 600;
        }
        .table-compare tr:hover td {
            background-color: #f5f7fa;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <div class="hero-gradient text-white py-20 px-4 sm:px-6 lg:px-8">
        <div class="max-w-5xl mx-auto text-center">
            <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">
                打车App如何秒级找到附近乘客
            </h1>
            <p class="text-xl md:text-2xl font-medium mb-8 opacity-90">
                揭秘GeoHash技术背后的精妙算法与应用实践
            </p>
            <div class="flex justify-center">
                <div class="w-24 h-1 bg-white opacity-50 rounded-full"></div>
            </div>
        </div>
    </div>

    <!-- Main Content -->
    <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <!-- Introduction -->
        <div class="mb-16">
            <p class="text-xl text-gray-700 mb-8 leading-relaxed">
                想象一下，当你打开打车App，点击"呼叫车辆"的那一刻，系统是如何在毫秒内从数万名司机中找到距离你最近的几个？这背后隐藏着一个看似简单却极其精妙的地理编码技术——GeoHash。
            </p>
            <div class="rounded-xl overflow-hidden shadow-lg">
                <img src="https://cdn.nlark.com/yuque/0/2025/png/21449790/1754111178592-927e8af8-ffee-4eb0-b958-d6849563cee0.png" alt="GeoHash示意图" class="w-full">
                <div class="bg-white p-4 text-sm text-gray-600">
                    图：GeoHash将地球表面划分为网格，相近位置有相似的编码前缀
                </div>
            </div>
            <p class="mt-6 text-gray-700 leading-relaxed">
                从滴滴到Uber，从美团到饿了么，几乎所有需要地理位置匹配的应用都在使用这项技术。它不仅能将地球表面的任意位置转换为短短的字符串，还能通过字符串的前缀匹配快速筛选出相邻区域。
            </p>
        </div>

        <!-- Section 1 -->
        <section class="mb-20">
            <h2 class="text-3xl font-bold mb-8 text-gray-800 flex items-center">
                <span class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white mr-4">1</span>
                GeoHash介绍
            </h2>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">1.1 从经纬度到字符串的魔法转换</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                传统的GPS定位系统给我们的是经纬度坐标，比如北京天安门广场的坐标是(116.3974, 39.9093)。但在实际应用中，我们经常需要回答这样的问题："距离这个位置1公里范围内有哪些司机？"如果直接用经纬度计算距离，需要对数据库中的每个司机位置都进行距离计算，这在数万甚至数十万司机的情况下，性能会非常差。
            </p>
            <p class="mb-6 text-gray-700 leading-relaxed">
                GeoHash的出现完美解决了这个问题。它将二维的经纬度坐标转换为一维的字符串，使得相近的地理位置拥有相似的前缀。
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：GeoHash编码实现</span>
                    <i class="fas fa-code"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>import java.util.*;

public class GeoHashEncoder {
    private static final String CHARS = "0123456789bcdefghjkmnpqrstuvwxyz";
    
    /**
     * 将经纬度编码为GeoHash字符串
     */
    public static String geohashEncode(double lat, double lon, int precision) {
        double[] latRange = {-90.0, 90.0};
        double[] lonRange = {-180.0, 180.0};
        
        StringBuilder geohash = new StringBuilder();
        int bit = 0;
        int ch = 0;
        
        while (geohash.length() < precision) {
            if (bit % 2 == 0) {
                double mid = (lonRange[0] + lonRange[1]) / 2;
                if (lon >= mid) {
                    ch |= 1 << (4 - bit % 5);
                    lonRange[0] = mid;
                } else {
                    lonRange[1] = mid;
                }
            } else {
                double mid = (latRange[0] + latRange[1]) / 2;
                if (lat >= mid) {
                    ch |= 1 << (4 - bit % 5);
                    latRange[0] = mid;
                } else {
                    latRange[1] = mid;
                }
            }
            
            bit++;
            if (bit % 5 == 0) {
                geohash.append(CHARS.charAt(ch));
                ch = 0;
            }
        }
        
        return geohash.toString();
    }
    
    public static void main(String[] args) {
        // 测试：北京天安门广场的GeoHash
        String result = geohashEncode(39.9093, 116.3974, 6);
        System.out.println("编码结果: " + result);  // 输出：wx4g0e
    }
}</code></pre>
                </div>
            </div>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">1.2 为什么需要GeoHash？</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                想象一下，如果没有GeoHash，打车平台要找到附近1公里内的司机需要这样做：
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>SQL示例：传统距离计算方式</span>
                    <i class="fas fa-database"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>-- 传统方式：计算每个司机到乘客的距离
SELECT driver_id, 
       (6371 * acos(cos(radians(39.9093)) * cos(radians(lat)) * 
        cos(radians(lon) - radians(116.3974)) + 
        sin(radians(39.9093)) * sin(radians(lat)))) AS distance
FROM drivers 
WHERE (6371 * acos(cos(radians(39.9093)) * cos(radians(lat)) * 
       cos(radians(lon) - radians(116.3974)) + 
       sin(radians(39.9093)) * sin(radians(lat)))) <= 1
ORDER BY distance;</code></pre>
                </div>
            </div>

            <p class="mb-6 text-gray-700 leading-relaxed">
                这个查询需要对数据库中的每个司机记录都进行复杂的三角函数计算，性能可想而知。
            </p>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">1.3 GeoHash vs 传统地理索引</h3>
            <div class="overflow-x-auto">
                <table class="table-compare">
                    <thead>
                        <tr>
                            <th>特性</th>
                            <th>传统距离计算</th>
                            <th>GeoHash</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>查询复杂度</td>
                            <td>O(n)</td>
                            <td>O(log n)</td>
                        </tr>
                        <tr>
                            <td>内存占用</td>
                            <td>高</td>
                            <td>低</td>
                        </tr>
                        <tr>
                            <td>实现难度</td>
                            <td>简单</td>
                            <td>中等</td>
                        </tr>
                        <tr>
                            <td>精度控制</td>
                            <td>灵活</td>
                            <td>固定</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </section>

        <!-- Section Divider -->
        <div class="section-divider"></div>

        <!-- Section 2 -->
        <section class="mb-20">
            <h2 class="text-3xl font-bold mb-8 text-gray-800 flex items-center">
                <span class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white mr-4">2</span>
                GeoHash算法深度解析
            </h2>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800">2.1 二分法编码原理</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                GeoHash的核心思想是"分而治之"。它将地球表面递归地划分为更小的区域，每个区域用一个字符表示。
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：GeoHash编码可视化</span>
                    <i class="fas fa-project-diagram"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>public class GeoHashVisualizer {
    public static void visualizeGeohashEncoding() {
        // 以经度为例，将-180到180度分为32个区间
        double[][] longitudeRanges = {
            {-180, -168.75}, {-168.75, -157.5}, {-157.5, -146.25},
            {-146.25, -135}, {-135, -123.75}, {-123.75, -112.5},
            {-112.5, -101.25}, {-101.25, -90}
        };
        
        String chars = "0123456789bcdefghjkmnpqrstuvwxyz";
        
        System.out.println("经度编码示例：");
        for (int i = 0; i < 8; i++) {
            System.out.printf("区间 %d: %.2f° 到 %.2f° -> 字符 '%c'%n", 
                            i, longitudeRanges[i][0], longitudeRanges[i][1], 
                            chars.charAt(i));
        }
    }
}</code></pre>
                </div>
            </div>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">2.2 精度与字符串长度的关系</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                GeoHash的精度由字符串长度决定：
            </p>

            <div class="overflow-x-auto">
                <table class="table-compare">
                    <thead>
                        <tr>
                            <th>长度</th>
                            <th>精度(纬度)</th>
                            <th>精度(经度)</th>
                            <th>实际距离</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1位</td>
                            <td>±23°</td>
                            <td>±23°</td>
                            <td>±2500km</td>
                        </tr>
                        <tr>
                            <td>2位</td>
                            <td>±2.8°</td>
                            <td>±5.6°</td>
                            <td>±630km</td>
                        </tr>
                        <tr>
                            <td>3位</td>
                            <td>±0.35°</td>
                            <td>±0.7°</td>
                            <td>±78km</td>
                        </tr>
                        <tr>
                            <td>4位</td>
                            <td>±0.087°</td>
                            <td>±0.087°</td>
                            <td>±20km</td>
                        </tr>
                        <tr>
                            <td>5位</td>
                            <td>±0.022°</td>
                            <td>±0.022°</td>
                            <td>±2.4km</td>
                        </tr>
                        <tr>
                            <td>6位</td>
                            <td>±0.0027°</td>
                            <td>±0.0054°</td>
                            <td>±610m</td>
                        </tr>
                        <tr>
                            <td>7位</td>
                            <td>±0.00068°</td>
                            <td>±0.00136°</td>
                            <td>±76m</td>
                        </tr>
                        <tr>
                            <td>8位</td>
                            <td>±0.000085°</td>
                            <td>±0.00017°</td>
                            <td>±19m</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">2.3 边界问题与解决方案</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                GeoHash有一个著名的边界问题：两个地理位置可能很近，但它们的GeoHash前缀完全不同。
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：相邻区域计算</span>
                    <i class="fas fa-map-marked-alt"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>public class GeoHashNeighbors {
    private static final Map<String, String[]> NEIGHBORS = new HashMap<>();
    
    static {
        NEIGHBORS.put("n", new String[]{"p0r21436x8zb9dcf5h7kjnmqesgutwvy", "bc01fg45238967deuvhjyznpkmstqrwx"});
        NEIGHBORS.put("s", new String[]{"14365h7k9dcfesgujnmqp0r2twvyx8zb", "238967debc01fg45kmstqrwxuvhjyznp"});
        NEIGHBORS.put("e", new String[]{"bc01fg45238967deuvhjyznpkmstqrwx", "p0r21436x8zb9dcf5h7kjnmqesgutwvy"});
        NEIGHBORS.put("w", new String[]{"238967debc01fg45kmstqrwxuvhjyznp", "14365h7k9dcfesgujnmqp0r2twvyx8zb"});
    }
    
    public static List<String> getNeighborGeohashes(String geohash) {
        // 实现相邻区域计算逻辑
        List<String> neighbors = new ArrayList<>();
        // 这里简化处理，实际需要完整的相邻区域映射表
        neighbors.add(geohash + "n");
        neighbors.add(geohash + "s");
        neighbors.add(geohash + "e");
        neighbors.add(geohash + "w");
        return neighbors;
    }
}</code></pre>
                </div>
            </div>
        </section>

        <!-- Section Divider -->
        <div class="section-divider"></div>

        <!-- Section 3 -->
        <section class="mb-20">
            <h2 class="text-3xl font-bold mb-8 text-gray-800 flex items-center">
                <span class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white mr-4">3</span>
                实际应用场景
            </h2>

            <div class="grid md:grid-cols-2 gap-8">
                <!-- 打车平台 -->
                <div class="feature-card bg-white">
                    <div class="p-6">
                        <div class="flex items-center mb-4">
                            <div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center text-blue-500 mr-4">
                                <i class="fas fa-car text-xl"></i>
                            </div>
                            <h3 class="text-xl font-semibold text-gray-800">打车平台的乘客匹配</h3>
                        </div>
                        <p class="text-gray-600 mb-4">
                            滴滴和Uber都使用GeoHash来快速筛选附近的司机：
                        </p>
                        <div class="code-block">
                            <div class="code-header">
                                <span>Java示例</span>
                            </div>
                            <div class="code-content p-4 overflow-x-auto">
                                <pre class="text-xs text-gray-300"><code>public List<Driver> findNearbyDrivers(double passengerLat, double passengerLon, double radiusKm) {
    // 1. 将乘客位置编码为GeoHash
    String passengerGeohash = GeoHashEncoder.geohashEncode(passengerLat, passengerLon, 6);
    
    // 2. 获取相邻的GeoHash区域
    List<String> nearbyGeohashes = GeoHashNeighbors.getNeighborGeohashes(passengerGeohash);
    nearbyGeohashes.add(passengerGeohash);
    
    // 3. 从Redis或数据库中快速查询
    List<Driver> nearbyDrivers = new ArrayList<>();
    for (String geohash : nearbyGeohashes) {
        List<Driver> drivers = queryDriversByGeohash(geohash);
        nearbyDrivers.addAll(drivers);
    }
    
    return nearbyDrivers;
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 外卖配送 -->
                <div class="feature-card bg-white">
                    <div class="p-6">
                        <div class="flex items-center mb-4">
                            <div class="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center text-green-500 mr-4">
                                <i class="fas fa-utensils text-xl"></i>
                            </div>
                            <h3 class="text-xl font-semibold text-gray-800">外卖配送的商家筛选</h3>
                        </div>
                        <p class="text-gray-600 mb-4">
                            美团外卖使用类似的技术来筛选配送范围内的商家：
                        </p>
                        <div class="code-block">
                            <div class="code-header">
                                <span>Java示例</span>
                            </div>
                            <div class="code-content p-4 overflow-x-auto">
                                <pre class="text-xs text-gray-300"><code>public List<Restaurant> findNearbyRestaurants(double userLat, double userLon, double radiusKm) {
    String userGeohash = GeoHashEncoder.geohashEncode(userLat, userLon, 6);
    
    // 获取用户周围的GeoHash区域
    List<String> searchAreas = GeoHashNeighbors.getNeighborGeohashes(userGeohash);
    searchAreas.add(userGeohash);
    
    List<Restaurant> nearbyRestaurants = new ArrayList<>();
    
    for (String area : searchAreas) {
        // 从数据库快速查询该区域的商家
        List<Restaurant> restaurants = restaurantRepository.findByGeohashStartingWith(area);
        
        // 精确计算距离并筛选
        for (Restaurant restaurant : restaurants) {
            double distance = calculateDistance(userLat, userLon, 
                                             restaurant.getLat(), restaurant.getLon());
            if (distance <= radiusKm) {
                nearbyRestaurants.add(restaurant);
            }
        }
    }
    
    return nearbyRestaurants;
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 社交应用 -->
                <div class="feature-card bg-white">
                    <div class="p-6">
                        <div class="flex items-center mb-4">
                            <div class="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center text-purple-500 mr-4">
                                <i class="fas fa-users text-xl"></i>
                            </div>
                            <h3 class="text-xl font-semibold text-gray-800">社交应用的附近好友</h3>
                        </div>
                        <p class="text-gray-600 mb-4">
                            微信、陌陌等社交应用也使用GeoHash来推荐附近的人：
                        </p>
                        <div class="code-block">
                            <div class="code-header">
                                <span>Java示例</span>
                            </div>
                            <div class="code-content p-4 overflow-x-auto">
                                <pre class="text-xs text-gray-300"><code>public List<UserLocation> findNearbyUsers(String userId, double radiusMeters) {
    UserLocation user = getUserLocation(userId);
    String userGeohash = GeoHashEncoder.geohashEncode(user.getLat(), user.getLon(), 7);
    
    // 使用Redis的GEO命令
    List<UserLocation> nearbyUsers = queryNearbyUsersFromRedis(
        user.getLon(), user.getLat(), radiusMeters);
    
    return nearbyUsers;
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 电商推荐 -->
                <div class="feature-card bg-white">
                    <div class="p-6">
                        <div class="flex items-center mb-4">
                            <div class="w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center text-orange-500 mr-4">
                                <i class="fas fa-shopping-cart text-xl"></i>
                            </div>
                            <h3 class="text-xl font-semibold text-gray-800">电商的本地化推荐</h3>
                        </div>
                        <p class="text-gray-600 mb-4">
                            淘宝、京东等电商平台使用GeoHash来推荐附近的商家和商品：
                        </p>
                        <div class="code-block">
                            <div class="code-header">
                                <span>Java示例</span>
                            </div>
                            <div class="code-content p-4 overflow-x-auto">
                                <pre class="text-xs text-gray-300"><code>public List<Product> recommendLocalProducts(UserLocation userLocation, String category) {
    String userGeohash = GeoHashEncoder.geohashEncode(userLocation.getLat(), userLocation.getLon(), 5);
    
    // 查询附近商家的商品
    Map<String, Object> query = new HashMap<>();
    query.put("geohash", "^" + userGeohash);
    query.put("status", "active");
    
    if (category != null) {
        query.put("category", category);
    }
    
    List<Product> localProducts = productRepository.findByQuery(query, 20);
    return localProducts;
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Section Divider -->
        <div class="section-divider"></div>

        <!-- Section 4 -->
        <section class="mb-20">
            <h2 class="text-3xl font-bold mb-8 text-gray-800 flex items-center">
                <span class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white mr-4">4</span>
                业务实践
            </h2>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800">4.1 Redis中的GeoHash实现</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                Redis从3.2版本开始内置了GEO命令，大大简化了GeoHash的使用：
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：Redis GEO命令使用</span>
                    <i class="fas fa-server"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>import redis.clients.jedis.Jedis;
import java.util.*;

public class RedisGeoService {
    private Jedis jedis;
    
    public RedisGeoService() {
        this.jedis = new Jedis("localhost", 6379);
    }
    
    // 添加司机位置
    public void addDriverLocation(String driverId, double lat, double lon) {
        jedis.geoadd("driver_locations", lon, lat, driverId);
    }
    
    // 查找附近司机
    public List<Map<String, Object>> findNearbyDrivers(double passengerLat, double passengerLon, double radiusKm) {
        List<Map<String, Object>> results = new ArrayList<>();
        
        // 使用Redis的georadius命令
        List<redis.clients.jedis.GeoRadiusResponse> responses = 
            jedis.georadius("driver_locations", passengerLon, passengerLat, radiusKm, 
                           redis.clients.jedis.params.GeoRadiusParam.geoRadiusParam()
                           .withDist().withCoord());
        
        for (redis.clients.jedis.GeoRadiusResponse response : responses) {
            Map<String, Object> result = new HashMap<>();
            result.put("driver_id", response.getMemberByString());
            result.put("distance", response.getDistance());
            result.put("coordinates", response.getCoordinate());
            results.add(result);
        }
        
        return results;
    }
}</code></pre>
                </div>
            </div>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">4.2 数据库索引策略</h3>
            <p class="mb-6 text-gray-700 leading-relaxed">
                在MySQL中使用GeoHash进行空间索引：
            </p>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：MySQL GeoHash索引</span>
                    <i class="fas fa-database"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>public void createDriversTable() {
    String sql = """
        CREATE TABLE drivers (
            id INT PRIMARY KEY,
            name VARCHAR(100),
            lat DECIMAL(10, 8),
            lon DECIMAL(11, 8),
            geohash VARCHAR(8),
            INDEX idx_geohash (geohash)
        )
        """;
    // 执行建表语句
}

public List<Driver> findNearbyDrivers(double passengerLat, double passengerLon, double radiusKm) {
    String passengerGeohash = GeoHashEncoder.geohashEncode(passengerLat, passengerLon, 6);
    
    String sql = """
        SELECT *, 
               (6371 * acos(cos(radians(?)) * cos(radians(lat)) * 
                cos(radians(lon) - radians(?)) + 
                sin(radians(?)) * sin(radians(lat)))) AS distance
        FROM drivers 
        WHERE geohash LIKE ? OR geohash LIKE ? OR geohash LIKE ?
        HAVING distance <= ?
        ORDER BY distance
        """;
    
    // 执行查询并返回结果
    return new ArrayList<>();
}</code></pre>
                </div>
            </div>

            <h3 class="text-2xl font-semibold mb-6 text-gray-800 mt-12">4.3 缓存优化技巧</h3>
            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：GeoHash查询缓存</span>
                    <i class="fas fa-bolt"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class GeoHashCache {
    private Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
    private final long TTL = 300000; // 5分钟缓存（毫秒）
    
    public List<Driver> getNearbyDrivers(double lat, double lon, double radiusKm) {
        // 生成缓存键
        String cacheKey = String.format("nearby_drivers:%.4f:%.4f:%.1f", lat, lon, radiusKm);
        
        // 检查缓存
        CacheEntry cachedEntry = cache.get(cacheKey);
        if (cachedEntry != null && System.currentTimeMillis() - cachedEntry.timestamp < TTL) {
            return cachedEntry.data;
        }
        
        // 计算附近司机
        List<Driver> nearbyDrivers = calculateNearbyDrivers(lat, lon, radiusKm);
        
        // 更新缓存
        cache.put(cacheKey, new CacheEntry(nearbyDrivers, System.currentTimeMillis()));
        
        return nearbyDrivers;
    }
}</code></pre>
                </div>
            </div>
        </section>

        <!-- Section Divider -->
        <div class="section-divider"></div>

        <!-- Section 5 -->
        <section>
            <h2 class="text-3xl font-bold mb-8 text-gray-800 flex items-center">
                <span class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white mr-4">5</span>
                实现GeoHash编码
            </h2>

            <div class="my-8 code-block">
                <div class="code-header">
                    <span>Java示例：完整GeoHash实现</span>
                    <i class="fas fa-code"></i>
                </div>
                <div class="code-content">
                    <pre class="text-gray-300"><code>import java.util.*;

public class GeoHash {
    private static final String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
    private static final int[] BITS = {16, 8, 4, 2, 1};
    
    /**
     * 将经纬度编码为GeoHash
     */
    public static String encode(double lat, double lon, int precision) {
        double[] latRange = {-90.0, 90.0};
        double[] lonRange = {-180.0, 180.0};
        
        StringBuilder geohash = new StringBuilder();
        int bit = 0;
        int ch = 0;
        
        while (geohash.length() < precision) {
            if (bit % 2 == 0) {
                double mid = (lonRange[0] + lonRange[1]) / 2;
                if (lon >= mid) {
                    ch |= BITS[bit % 5];
                    lonRange[0] = mid;
                } else {
                    lonRange[1] = mid;
                }
            } else {
                double mid = (latRange[0] + latRange[1]) / 2;
                if (lat >= mid) {
                    ch |= BITS[bit % 5];
                    latRange[0] = mid;
                } else {
                    latRange[1] = mid;
                }
            }
            
            bit++;
            if (bit % 5 == 0) {
                geohash.append(BASE32.charAt(ch));
                ch = 0;
            }
        }
        
        return geohash.toString();
    }
    
    /**
     * 将GeoHash解码为经纬度
     */
    public static double[] decode(String geohash) {
        double[] latRange = {-90.0, 90.0};
        double[] lonRange = {-180.0, 180.0};
        
        int bit = 0;
        for (char c : geohash.toCharArray()) {
            if (BASE32.indexOf(c) == -1) {
                throw new IllegalArgumentException("Invalid geohash character: " + c);
            }
            
            int ch = BASE32.indexOf(c);
            for (int i = 0; i < 5; i++) {
                if (bit % 2 == 0) {
                    double mid = (lonRange[0] + lonRange[1]) / 2;
                    if ((ch & BITS[i]) != 0) {
                        lonRange[0] = mid;
                    } else {
                        lonRange[1] = mid;
                    }
                } else {
                    double mid = (latRange[0] + latRange[1]) / 2;
                    if ((ch & BITS[i]) != 0) {
                        latRange[0] = mid;
                    } else {
                        latRange[1] = mid;
                    }
                }
                bit++;
            }
        }
        
        double lat = (latRange[0] + latRange[1]) / 2;
        double lon = (lonRange[0] + lonRange[1]) / 2;
        
        return new double[]{lat, lon};
    }
    
    public static void main(String[] args) {
        String encoded = encode(39.9093, 116.3974, 6);
        System.out.println("编码结果: " + encoded);  // wx4g0e
        
        double[] decoded = decode(encoded);
        System.out.printf("解码结果: (%.4f, %.4f)%n", decoded[0], decoded[1]);
    }
}</code></pre>
                </div>
            </div>
        </section>
    </div>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>
```